// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: MIT

import { BaseDialog } from "./dialog.slint";
import { Icons } from "../icons/icons.slint";
import { MaterialText } from "./material_text.slint";
import { MaterialStyleMetrics } from "../styling/material_style_metrics.slint";
import { MaterialTypography } from "../styling/material_typography.slint";
import { MaterialPalette } from "../styling/material_palette.slint";
import { StateLayerArea } from "state_layer.slint";

component TimeSelector inherits Rectangle {
    in property <bool> selected;
    in property <int> value;

    callback clicked <=> touch_area.clicked;

    width: max(MaterialStyleMetrics.size_48, text_label.min_width);
    height: max(MaterialStyleMetrics.size_48, text_label.min_height);
    border_radius: max(root.width, root.height) / 2;
    vertical_stretch: 0;
    horizontal_stretch: 0;

    touch_area := TouchArea {
        text_label := MaterialText {
            text: root.value;
            vertical_alignment: center;
            horizontal_alignment: center;
            style: MaterialTypography.body_large;
            color: MaterialPalette.on_surface;
        }
    }

    states [
        selected when root.selected: {
            text_label.color: MaterialPalette.on_primary;
        }
    ]
}

component Clock {
    in property <[int]> model;
    in property <bool> two_columns;
    in property <int> total;
    in_out property <int> current_index;
    in property <int> current_value;

    callback current_index_changed(index: int);

    property <length> radius: max(root.width, root.height) / 2;
    property <length> picker_ditameter: MaterialStyleMetrics.size_48;
    property <length> center: root.radius - root.picker_ditameter / 2;
    property <length> outer_padding: 2px;
    property <length> inner_padding: 32px;
    property <length> radius_outer: root.center - root.outer_padding;
    property <length> radius_inner: root.center - root.inner_padding;
    property <int> half_total: root.total / 2;
    property <angle> rotation: 0.25turn;
    property <length> current_x: get_index_x(root.current_value);
    property <length> current_y: get_index_y(root.current_value);

    min_width: MaterialStyleMetrics.size_256;
    min_height: self.min_width;
    vertical_stretch: 0;
    horizontal_stretch: 0;

    background_layer := Rectangle {
        border_radius: max(self.width, self.height) / 2;
        background: MaterialPalette.surface_container_highest;

        if root.current_index >= 0 || root.current_index < root.model.length: Path {
            stroke: MaterialPalette.primary;
            stroke_width: 2px;
            viewbox_width: self.width / 1px;
            viewbox_height: self.height / 1px;

            MoveTo {
                x: root.width / 2px;
                y: root.height / 2px;
            }

            LineTo {
                x: (root.current_x + root.picker_ditameter / 2) / 1px;
                y: (root.current_y + root.picker_ditameter / 2) / 1px;
            }
        }

        Rectangle {
            width: MaterialStyleMetrics.size_8;
            height: self.width;
            background: MaterialPalette.primary;
            border_radius: self.width / 2;
        }

        if root.current_index < root.model.length: Rectangle {
            x: root.current_x;
            y: root.current_y;
            width: root.picker_ditameter;
            height: root.picker_ditameter;
            border_radius: root.picker_ditameter / 2;
                background: MaterialPalette.primary;

            if root.current_index < 0: Rectangle {
                width: MaterialStyleMetrics.size_4;
                height: self.width;
                border_radius: self.width / 2;
                background: MaterialPalette.on_primary;
            }
        }

        for val[index] in root.model: TimeSelector {
            x: get_index_x(val);
            y: get_index_y(val);
            width: root.picker_ditameter;
            height: root.picker_ditameter;
            value: val;
            selected: index == root.current_index;
            accessible_role: button;
            accessible_label: @tr("{} Hours or minutes of {}", val, root.total);
            accessible_action_default => {
                self.clicked();
            }

            clicked => {
                root.set_current_index(index);
            }
        }
    }


    pure function value_to_angle(value: int) -> angle {
        if root.two_columns {
            if value >= root.half_total {
                return clamp((value - root.half_total) / root.half_total * 1turn, 0, 0.999999turn) - root.rotation;
            }
            return clamp(value / root.half_total * 1turn, 0, 0.99999turn) - root.rotation;
        }

        clamp(value / root.total * 1turn, 0, 0.99999turn) - root.rotation
    }

    pure function get_index_x(value: int) -> length {
        if root.two_columns && value >= root.half_total {
            return root.center + (root.radius_inner / 1px * cos(root.value_to_angle(value))) * 1px;
        }

        root.center + (root.radius_outer / 1px * cos(root.value_to_angle(value))) * 1px
    }

    pure function get_index_y(value: int) -> length {
        // this is only for 24 mode
        if root.total == 24 && value == 0 {
            return root.center + (root.radius_inner / 1px * sin(root.value_to_angle(value))) * 1px;
        }
        if root.total == 24 && value == 12 {
            return root.center + (root.radius_outer / 1px * sin(root.value_to_angle(value))) * 1px;
        }
        if root.two_columns && value >= root.half_total {
            return root.center + (root.radius_inner / 1px * sin(root.value_to_angle(value))) * 1px;
        }

        root.center + (root.radius_outer / 1px * sin(root.value_to_angle(value))) * 1px
    }

    function set_current_index(index: int) {
        root.current_index_changed(index);
    }
}

component TimePickerInput {
    in property <bool> read_only <=> text_input.read_only;
    in property <bool> checked;
    in_out property <string> text <=> text_input.text;

    callback clicked;
    callback edited(value: int);

    width: MaterialStyleMetrics.size_90;
    min_height: max(MaterialStyleMetrics.size_80, layout.min_height);
    vertical_stretch: 0;
    horizontal_stretch: 0;

    forward_focus: text_input;

    background_layer := Rectangle {
        border_radius: MaterialStyleMetrics.border_radius_8;
        background: MaterialPalette.surface_container_highest;
        clip: true;

        layout := HorizontalLayout {
            padding: MaterialStyleMetrics.padding_8;

            text_input := TextInput {
                vertical_alignment: center;
                horizontal_alignment: center;
                color: MaterialPalette.on_surface;
                font_size: MaterialTypography.display_large.font_size;
                font_weight: MaterialTypography.display_large.font_weight;
                input_type: number;
                visible: !root.read_only;

                edited => {
                    root.edited(self.text.to_float());
                }
            }
        }

        if root.read_only : StateLayerArea {
            border_radius: background_layer.border_radius;
            color: MaterialPalette.on_surface;

            HorizontalLayout {
                padding: MaterialStyleMetrics.padding_8;

                MaterialText {
                    text: root.text.to_float() >= 10 ? root.text : "0" + root.text;
                    style: MaterialTypography.display_large;
                    color: MaterialPalette.on_surface;
                    horizontal_alignment: center;
                    vertical_alignment: center;
                }
            }

            clicked => {
                root.clicked();
            }
        }
    }

    states [
        has_focus when text_input.has_focus && !root.read_only: {
            background_layer.background: MaterialPalette.primary_container;
            background_layer.border_width: 2px;
            background_layer.border_color: MaterialPalette.primary;
            text_input.color: MaterialPalette.on_primary_container;
        }
        checked when root.checked: {
            background_layer.background: MaterialPalette.primary_container;
            text_input.color: MaterialPalette.on_primary_container;
        }
    ]
}

component PeriodSelectorItem {
    in property <string> text <=> label.text;
    in property <bool> checked;

    callback clicked <=> state_layer.clicked;

    background_layer := Rectangle {
        state_layer := StateLayerArea {
            color: label.color;

            label := MaterialText {
                color: MaterialPalette.on_surface_variant;
                horizontal_alignment: center;
            }
        }
    }

    states [
        checked when root.checked: {
            background_layer.background: MaterialPalette.tertiary_container;
            label.color: MaterialPalette.on_tertiary_container;
        }
    ]
}


component PeriodSelector {
    in property <bool> am_selected;
    in property <bool> horizontal;

    callback update_period(am_selected: bool);

    min_width: max(MaterialStyleMetrics.size_52, layout.min_width);
    min_height: max(root.horizontal ? MaterialStyleMetrics.size_38 : MaterialStyleMetrics.size_80, layout.min_height);
    accessible_label: "AM or PM";
    accessible_role: checkbox;
    accessible_checked: root.am_selected;

    Rectangle {
        border_radius: border.border_radius;
        clip: true;

        layout := VerticalLayout {
            if root.horizontal : HorizontalLayout {
                PeriodSelectorItem {
                    text: "AM";
                    checked: root.am_selected;

                    clicked => {
                        if root.am_selected {
                            return;
                        }
                        root.update_period(true);
                    }
                }

                Rectangle {
                    width: 1px;
                    background: border.border_color;
                    vertical_stretch: 0;
                }

                PeriodSelectorItem {
                    text: "PM";
                    checked: !root.am_selected;

                    clicked => {
                        if !root.am_selected {
                            return;
                        }
                        root.update_period(false);
                    }
                }
            }

            if !root.horizontal : VerticalLayout {
                PeriodSelectorItem {
                    text: "AM";
                    checked: root.am_selected;

                    clicked => {
                        if root.am_selected {
                            return;
                        }
                        root.update_period(true);
                    }
                }

                Rectangle {
                    height: 1px;
                    background: border.border_color;
                    vertical_stretch: 0;
                }

                PeriodSelectorItem {
                    text: "PM";
                    checked: !root.am_selected;

                    clicked => {
                        if !root.am_selected {
                            return;
                        }
                        root.update_period(false);
                    }
                }
            }
        }
    }

    border := Rectangle {
        border_radius: MaterialStyleMetrics.border_radius_8;
        border_width: 1px;
        border_color: MaterialPalette.outline;
    }
}

export struct Time {
    hour: int,
    minute: int,
    second: int
}

export component TimePickerPopup inherits PopupWindow {
    in property <bool> use_24_hour_format: true;
    in property <string> title: @tr("Select time");
    in property <string> cancel_text: @tr("Cancel");
    in property <string> ok_text: @tr("Ok");
    in property <Time> time: { hour: 12 };
    in property <string> hour_label: @tr("Hour");
    in property <string> minute_label: @tr("Minute");

    property <bool> keyboard_mode;
    property <bool> minutes_selected;
    property <bool> am_selected: true;
    property <[int]> twelfth_hour_model: [12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
    property <[int]> use-24_hour_format_model: [
        12,
        1,
        2,
        3,
        4,
        5,
        6,
        7,
        8,
        9,
        10,
        11,
        0,
        13,
        14,
        15,
        16,
        17,
        18,
        19,
        20,
        21,
        22,
        23
    ];
    property <[int]> minute_model: [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55];
    property <[int]> current_model: root.get_current_model();
    property <int> current_index: root.minutes_selected ? root.index_of_minute(root.current_time.minute) : root.index_of_hour(root.current_time.hour);
    property <Time> current_time: root.time;
    property <int> time_picker_hour: hour_time_picker.text.to_float();
    property <int> time_picker_minute:  minute_time_picker.text.to_float();
    property <bool> horizontal: root.width >= MaterialStyleMetrics.size_572;

    callback ok();

    close_policy: no_auto_close;
    forward_focus: base;

    base := BaseDialog {
        width: 100%;
        height: 100%;
        title: root.title;
        action_button_icons: [
            root.keyboard_mode ? Icons.schedule : Icons.keyboard
        ];
        actions: [
            root.cancel_text,
            root.ok_text
        ];

        layout := HorizontalLayout {
            spacing: MaterialStyleMetrics.spacing_52;

            VerticalLayout {
                alignment: center;
                spacing: MaterialStyleMetrics.spacing_8;

                HorizontalLayout {
                    alignment: center;

                    hour_time_picker := TimePickerInput {
                        read_only: !root.keyboard_mode;
                        checked: self.read_only && !root.minutes_selected;
                        text: root.current_time.hour;

                        accessible_role: AccessibleRole.text_input;
                        accessible_value: self.text;
                        accessible_label: "hour";

                        clicked => {
                            root.minutes_selected = false;
                        }
                    }

                    separator := MaterialText {
                        min_width: MaterialStyleMetrics.size_24;
                        text: ":";
                        color: MaterialPalette.on_surface;
                        style: MaterialTypography.display_large;
                        vertical_alignment: center;
                        horizontal_alignment: center;
                    }

                    minute_time_picker := TimePickerInput {
                        read_only: !root.keyboard_mode;
                        checked: self.read_only && root.minutes_selected;
                        text: root.current_time.minute;

                        accessible_role: AccessibleRole.text_input;
                        accessible_value: self.text;
                        accessible_label: "minute";

                        clicked => {
                            root.minutes_selected = true;
                        }
                    }

                    // spacer
                    if !root.use_24_hour_format && (!root.horizontal || root.keyboard_mode) : Rectangle {
                        width: MaterialStyleMetrics.spacing_12;
                    }

                    if !root.use_24_hour_format && (!root.horizontal || root.keyboard_mode) : PeriodSelector {
                        am_selected: root.am_selected;

                        update-period(am) => {
                            root.am-selected = am;
                        }
                    }
                }

                // labels
                if root.keyboard_mode : HorizontalLayout {
                    alignment: start;

                    MaterialText {
                        width: hour_time_picker.width;
                        text: root.hour_label;
                        color: MaterialPalette.on_surface_variant;
                    }

                    Rectangle {
                        width: MaterialStyleMetrics.size_24;
                    }

                    MaterialText {
                        text: root.minute_label;
                        color: MaterialPalette.on_surface_variant;
                    }
                }

                if !root.use_24_hour_format && !root.keyboard_mode && root.horizontal : PeriodSelector {
                    am_selected: root.am_selected;
                    horizontal: true;

                    update-period(am) => {
                        root.am-selected = am;
                    }
                }
            }

            if root.horizontal && !root.keyboard_mode : Clock {
                model: root.current_model;
                two_columns: !root.minutes_selected && root.use-24_hour_format;
                current_index: root.current_index;
                total: root.minutes_selected ? 60 : root.use-24_hour_format ? 24 : 12;
                current_value: root.minutes_selected ? root.current_time.minute : root.current_time.hour;

                current_index_changed(index) => {
                    root.update_time_by_item(index);

                    if !root.minutes_selected {
                        root.minutes_selected = true;
                    }
                }
            }
        }

        // spacer
        if !root.horizontal && !root.keyboard_mode : Rectangle {
            height: MaterialStyleMetrics.spacing_12;
        }

        if !root.horizontal && !root.keyboard_mode : Clock {
            model: root.current_model;
            two_columns: !root.minutes_selected && root.use-24_hour_format;
            current_index: root.current_index;
            total: root.minutes_selected ? 60 : root.use-24_hour_format ? 24 : 12;
            current_value: root.minutes_selected ? root.current_time.minute : root.current_time.hour;
            height: self.width;

            current_index_changed(index) => {
                root.update_time_by_item(index);

                if !root.minutes_selected {
                    root.minutes_selected = true;
                }
            }
        }

        action_button_clicked(index) => {
            if index == 0 {
                root.keyboard_mode = !root.keyboard_mode;
            }
        }

        action_clicked(index) => {
            // cancel
            if index == 0 {
                root.close();
            } else if index == 1 {
                root.ok();
            }
        }

        close => {
            root.close();
        }
    }

    pure public function ok_enabled() -> bool {
        if !root.keyboard_mode {
            return root.current_time.hour <= 23 && root.current_time.minute <= 59;
        }

        if root.use-24_hour_format {
            return root.time_picker_hour <= 23 && root.time_picker_minute <= 59;
        }

        root.time_picker_hour <= 12 && root.time_picker_minute <= 59
    }

    public function get_current_time() -> Time {
        if root.keyboard_mode {
            if !root.use-24_hour_format && !root.am_selected {
                if root.current_time.hour == 12 {
                    return { hour: 0, minute: root.current_time.minute };
                }

                return { hour: root.current_time.hour + 12, minute: root.current_time.minute };
            }

            return root.current_time;
        }

        return { hour: root.time_picker_hour, minute: root.time_picker_minute };
    }

    changed keyboard_mode => {
        if root.keyboard_mode {
            return;
        }

        root.update_time(min(root.time_picker_hour, root.use-24_hour_format ? 23 : 12), min(root.time_picker_minute, 59));
    }

    changed time => {
        root.current_time = root.time;
    }

    function update_time_by_item(index: int) {
        root.update_time_by_value(root.current_model[index]);
    }

    function update_time_by_value(value: int) {
        if root.minutes_selected {
            root.update_time(root.current_time.hour, value);
            return;
        }

        root.update_time(value, root.current_time.minute);
    }

    function update_time(hour: int, minute: int) {
        root.current_time = { hour: hour, minute: minute };
        hour_time_picker.text = root.current_time.hour;
        minute_time_picker.text = minute;
    }

    pure function index_of_minute(minute: int) -> int {
        if mod(minute, 5) != 0 {
            return -1;
        }

        minute / 5
    }

    pure function index_of_hour(hour: int) -> int {
        if hour == 12 {
            return 0;
        }

        if hour == 0 {
            return 12;
        }

        if !root.use-24_hour_format && hour > 12 {
            return hour - 12;
        }

        hour
    }

    pure function get_current_model() -> [int] {
        if root.minutes_selected {
            return root.minute_model;
        }

        if root.use-24_hour_format {
            return root.use-24_hour_format_model;
        }

        root.twelfth_hour_model
    }
}
